<?php

namespace maotool\core;

use maotool\aop\ContainerProxyClass;
use maotool\aop\ProxyClassCallback;
use maotool\core\annotation\old\Autowired;
use maotool\core\console\command\Gui;
use maotool\core\console\command\Help;
use maotool\core\console\command\Image;
use maotool\core\console\command\Lists;
use maotool\core\console\command\Logs;
use maotool\core\console\command\thread\Thread;
use maotool\core\console\command\Version;
use maotool\core\console\command\Wechat;
use maotool\core\response\R;
use maotool\exception\RuntimeException;
use maotool\lock\FileLock;
use maotool\object\ObjUtil;
use maotool\object\ReflectionUtil;
use maotool\object\StrUtil;
use maotool\util\ComposerUtil;
use maotool\util\EnvUtil;
use maotool\util\MaoToolUtil;
use maotool\util\ProjectUtil;

/**
 * 容器化管理实例类
 * 设计模式为单例
 * 参考的Thinkphp 和 Spring boot 的 Bean
 */
class Container
{

    /**
     * 容器对象实例
     * @var Container
     */
    protected static $container = null;

    /**
     * 核心字段
     * 容器化工厂存储的容器
     * @var Instance[]
     */
    protected $instances = [];

    /**
     * 初始化绑定的容器
     * @var array
     */
    protected $initBinds = [];

    protected function __construct()
    {
        $this->initBinds = [
            ["concrete" => Env::class, "name" => null, "cover" => true, "vars" => [], "init" => false, "newInstance" => false, "remarks" => "环境变量"],
            ["concrete" => Config::class, "name" => null, "cover" => true, "vars" => [], "init" => false, "newInstance" => false, "remarks" => "配置",
                "construct" => function ($vars, $instance) {
                    /** @var Config $instance */
                    if (ProjectUtil::isFastAdmin()) {
                        $instance->set("framework.name", "fastAdmin");
                        $instance->set("framework.version", ComposerUtil::get("require")["karsonzhang/fastadmin-addons"]);
                        $instance->setProxy("database", function () {
                            $database = ReflectionUtil::method(["think\Config", "get"], ["database"]);
                            return [
                                'default' => 'mysql',
                                'connections' => [
                                    'mysql' => [
                                        "type" => $database["type"],
                                        "hostname" => $database["hostname"],
                                        "database" => $database["database"],
                                        "username" => $database["username"],
                                        "password" => $database["password"],
                                        "hostport" => $database["hostport"],
                                        "params" => $database["params"],
                                        "charset" => $database["charset"],
                                        "prefix" => $database["prefix"],
                                        "debug" => $database["debug"],
                                    ],
                                ],
                            ];
                        });
                        $instance->set("framework.runtime.path", "project:/runtime");
                        $instance->set("framework.runtime.temp.path", "project:/runtime/temp");
                        $instance->set("framework.runtime.log.path", "project:/runtime/log");
                        $instance->set("framework.runtime.cache.path", "project:/runtime/cache");
                        $instance->set("framework.storage.path", "project:/storage");
                        $instance->setProxy("framework.debug", function () {
                            return ReflectionUtil::method(["think\Config", "get"], ["app_debug"]);
                        });
                        $instance->set("framework.runtime.lock.path", "project:/runtime/lock");
                        $instance->set("framework.runtime.thread.path", "project:/runtime/thread");
                    } else if (ProjectUtil::isThinkphp()) {
                        $instance->set("framework.name", "thinkphp");
                        $instance->set("framework.version", ComposerUtil::get("require")["topthink/framework"]);
                        $instance->setProxy("database", function () {
                            $database = ReflectionUtil::method(["think\Config", "get"], ["database"]);
                            return [
                                'default' => 'mysql',
                                'connections' => [
                                    'mysql' => [
                                        "type" => $database["type"],
                                        "hostname" => $database["hostname"],
                                        "database" => $database["database"],
                                        "username" => $database["username"],
                                        "password" => $database["password"],
                                        "hostport" => $database["hostport"],
                                        "params" => $database["params"],
                                        "charset" => $database["charset"],
                                        "prefix" => $database["prefix"],
                                        "debug" => $database["debug"],
                                    ],
                                ],
                            ];
                        });
                        $instance->set("framework.runtime.path", "project:/runtime");
                        $instance->set("framework.runtime.temp.path", "project:/runtime/temp");
                        $instance->set("framework.runtime.log.path", "project:/runtime/log");
                        $instance->set("framework.runtime.cache.path", "project:/runtime/cache");
                        $instance->set("framework.storage.path", "project:/storage");
                        $instance->setProxy("framework.debug", function () {
                            return ReflectionUtil::method(["think\Config", "get"], ["app_debug"]);
                        });
                        $instance->set("framework.runtime.lock.path", "project:/runtime/lock");
                        $instance->set("framework.runtime.thread.path", "project:/runtime/thread");
                    } else if (ProjectUtil::isLaravel()) {
                        $instance->set("framework.name", "laravel");
                        $instance->set("framework.version", ProjectUtil::getVersion());
                        $instance->set("framework.runtime.path", "project:/storage");
                        $instance->set("framework.runtime.temp.path", "project:/storage/temp");
                        $instance->set("framework.runtime.log.path", "project:/storage/logs");
                        $instance->set("framework.runtime.cache.path", "project:/storage/cache");
                        $instance->set("framework.storage.path", "project:/storage");
                        $instance->set("framework.runtime.lock.path", "project:/storage/lock");
                        $instance->set("framework.runtime.thread.path", "project:/storage/thread");
                        $instance->setProxy("framework.debug", function () {
                            $instance = ReflectionUtil::method(["Illuminate\Container\Container", "getInstance"]);
                            $app = $instance->make("config")->get('app');
                            return isset($app['APP_DEBUG']) ? $app['APP_DEBUG'] : false;
                        });
                        $instance->setProxy("database", function () {
                            $instance = ReflectionUtil::method(["Illuminate\Container\Container", "getInstance"]);
                            $database = $instance->make("config")->get('database');
                            $mysql = $database['connections']['mysql'];
                            return [
                                'default' => 'mysql',
                                'connections' => [
                                    'mysql' => [
                                        "type" => isset($mysql["type"]) ? $mysql["type"] : 'mysql',
                                        "hostname" => isset($mysql["host"]) ? $mysql["host"] : 'localhost',
                                        "database" => isset($mysql["database"]) ? $mysql["database"] : 'fore',
                                        "username" => isset($mysql["username"]) ? $mysql["username"] : 'root',
                                        "password" => isset($mysql["password"]) ? $mysql["password"] : 'root',
                                        "hostport" => isset($mysql["port"]) ? $mysql["port"] : '3306',
                                        "params" => isset($mysql["params"]) ? $mysql["params"] : null,
                                        "charset" => isset($mysql["charset"]) ? $mysql["charset"] : 'utf8',
                                        "prefix" => isset($mysql["prefix"]) ? $mysql["prefix"] : '',
                                        "debug" => isset($mysql["debug"]) ? $mysql["debug"] : false,
                                    ],
                                ],
                            ];
                        });
                    } else {
                        $instance->set("framework.name", "maotool");
                        $instance->set("framework.version", ProjectUtil::getVersion());
                        $database = \maotool\core\facade\Env::get("database");
                        if (isset($database)) {
                            $instance->set("database", [
                                'default' => 'mysql',
                                'connections' => [
                                    'mysql' => [
                                        "type" => $database["type"],
                                        "hostname" => $database["hostname"],
                                        "database" => $database["database"],
                                        "username" => $database["username"],
                                        "password" => $database["password"],
                                        "hostport" => $database["hostport"],
                                        "charset" => $database["charset"],
                                    ],
                                ],
                            ]);
                        }
                    }
                    $instance->set("maoTool.version", MaoToolUtil::getVersion());
                    $instance->set("framework.runtime.path", "project:/runtime");
                    $instance->set("framework.runtime.temp.path", "project:/runtime/temp");
                    $instance->set("framework.runtime.log.path", "project:/runtime/log");
                    $instance->set("framework.runtime.cache.path", "project:/runtime/cache");
                    $instance->set("framework.runtime.lock.path", "project:/runtime/lock");
                    $instance->set("framework.storage.path", "project:/storage");
                    $instance->set("framework.runtime.thread.path", "project:/runtime/thread");
                    $instance->set("framework.debug", EnvUtil::get("debug", false));
                }],
            ["concrete" => Cache::class, "name" => null, "cover" => true, "vars" => [], "init" => false, "newInstance" => false, "remarks" => "缓存", "construct" => function ($vars, $instance) {
                if (ProjectUtil::isFastAdmin()) {
                    static::set(["concrete" => \maotool\cache\driver\ThinkPhpDriver::class, "cover" => false, "vars" => [], "init" => true, "newInstance" => false]);
                } else if (ProjectUtil::isThinkphp()) {
                    static::set(["concrete" => \maotool\cache\driver\ThinkPhpDriver::class, "cover" => false, "vars" => [], "init" => true, "newInstance" => false]);
                } else if (ProjectUtil::isLaravel()) {
                    static::set(["concrete" => \maotool\cache\driver\LaravelDriver::class, "cover" => false, "vars" => [], "init" => true, "newInstance" => false]);
                } else {
                    static::set(["concrete" => \maotool\cache\driver\FileDriver::class, "cover" => false, "vars" => [], "init" => true, "newInstance" => false]);
                }
            }],
            ["concrete" => FileLock::class, "name" => null, "cover" => true, "vars" => [], "init" => false, "newInstance" => false, "remarks" => "文件锁"],
            ["concrete" => Console::class, "name" => null, "cover" => true, "vars" => [], "init" => false, "newInstance" => false, "remarks" => "命令行", "construct" => function ($vars, $instance) {
                static::set(["concrete" => Thread::class, "cover" => false, "vars" => [], "init" => true, "newInstance" => false]);
                static::set(["concrete" => Lists::class, "cover" => false, "vars" => [], "init" => true, "newInstance" => false]);
                static::set(["concrete" => Version::class, "cover" => false, "vars" => [], "init" => true, "newInstance" => false]);
                static::set(["concrete" => Gui::class, "cover" => false, "vars" => [], "init" => true, "newInstance" => false]);
                static::set(["concrete" => Logs::class, "cover" => false, "vars" => [], "init" => true, "newInstance" => false]);
                static::set(["concrete" => Wechat::class, "cover" => false, "vars" => [], "init" => true, "newInstance" => false]);
                static::set(["concrete" => Image::class, "cover" => false, "vars" => [], "init" => true, "newInstance" => false]);
            }],
            ["concrete" => Session::class, "name" => null, "cover" => true, "vars" => [], "init" => false, "newInstance" => false, "remarks" => "会话", "construct" => function ($vars, $instance) {
                if (ProjectUtil::isFastAdmin()) {
                    static::set(["concrete" => \maotool\core\session\driver\ThinkPhpDriver::class, "cover" => false, "vars" => [], "init" => true, "newInstance" => false]);
                } else if (ProjectUtil::isThinkphp()) {
                    static::set(["concrete" => \maotool\core\session\driver\ThinkPhpDriver::class, "cover" => false, "vars" => [], "init" => true, "newInstance" => false]);
                } else if (ProjectUtil::isLaravel()) {
                    static::set(["concrete" => \maotool\core\session\driver\LaravelDriver::class, "cover" => false, "vars" => [], "init" => true, "newInstance" => false]);
                } else {
                    static::set(["concrete" => \maotool\core\session\driver\LocalDriver::class, "cover" => false, "vars" => [], "init" => true, "newInstance" => false]);
                }
            }],
            ["concrete" => R::class, "name" => null, "cover" => true, "vars" => [], "init" => false, "newInstance" => false, "remarks" => "返回值"],
        ];

    }

    /**
     * 获取当前容器的实例（单例）
     * @return Container
     */
    public static function getContainer()
    {
        if (is_null(static::$container)) {
            $log_time_start = microtime(true);
            static::setContainer(new static());
            foreach (static::$container->initBinds as $initBind) {
                static::set($initBind);
            }
//            \maotool\log\Console::debug('框架容器组 初始加载用时：' . number_format(microtime(true) - $log_time_start, 10) . ' 秒。');
        }
        return static::$container;
    }

    /**
     * 设置当前容器的实例
     * @access public
     * @param $instance
     * @return void
     */
    public static function setContainer($instance)
    {
        static::$container = $instance;
    }

    /**
     * 获取容器中的对象实例
     * @access public
     * @param string $name
     * @return mixed
     */
    public static function get($name)
    {
        return static::getName($name);
    }

    /**
     * 获取容器中的对象实例
     * @access public
     * @param string $name
     * @return mixed
     */
    public static function getName($name)
    {
        $result = null;
        $instances = static::getContainer()->instances;
        foreach ($instances as $instance) {
            if (StrUtil::equals(StrUtil::lowerCase($instance->name), StrUtil::lowerCase($name))) {
                $result = $instance->proxy;
            }
        }
        if (ObjUtil::isNotNull($result)) {
            return $result;
        }
        throw new RuntimeException("undefined instances");
    }

    /**
     * 获取容器中的对象实例
     * @access public
     * @param string $annotation
     * @return mixed
     */
    public static function getAnnotation($annotation)
    {
        $result = null;
        $instances = static::getContainer()->instances;
        foreach ($instances as $instance) {
            foreach ($instance->annotations as $item) {
                if (StrUtil::equals($item->name, $annotation)) {
                    $result = $instance->proxy;
                }
            }
        }
        if (ObjUtil::isNotNull($result)) {
            return $result;
        }
        throw new RuntimeException("undefined instances");
    }

    /**
     * 获取容器中的对象实例通过接口类
     * @param string $annotation
     * @return array
     */
    public static function getAnnotations($annotation)
    {
        $results = [];
        $instances = static::getContainer()->instances;
        foreach ($instances as $instance) {
            $isExist = false;
            foreach ($instance->annotations as $item) {
                if (StrUtil::equals(ReflectionUtil::getClass($item), $annotation)) {
                    $isExist = true;
                }
            }
            if ($isExist) {
                $results[] = $instance->proxy;
            }
        }
        return $results;
    }

    /**
     * 获取容器中的对象实例通过接口类
     * @param string $interface
     * @return mixed
     */
    public static function getInterface($interface)
    {
        $result = null;
        $instances = static::getContainer()->instances;
        foreach ($instances as $instance) {
            foreach ($instance->interfaces as $item) {
                if (StrUtil::equals($item->name, $interface)) {
                    $result = $instance->proxy;
                }
            }
        }
        if (ObjUtil::isNotNull($result)) {
            return $result;
        }
        throw new RuntimeException("undefined instances");
    }

    /**
     * 获取容器中的对象实例通过接口类
     * @param string $interface
     * @return array
     */
    public static function getInterfaces($interface)
    {
        $results = [];
        $instances = static::getContainer()->instances;
        foreach ($instances as $instance) {
            $isExist = false;
            foreach ($instance->interfaces as $item) {
                if (StrUtil::equals($item->name, $interface)) {
                    $isExist = true;
                }
            }
            if ($isExist) {
                $results[] = $instance->proxy;
            }
        }
        return $results;
    }

    /**
     * 获取容器中的对象实例
     * @param string $class
     * @param string|null $method
     * @return mixed
     */
    public static function getClass($class, $method = null)
    {
        if ($method !== null) {
            $class = $class . "::" . $method;
        }
        $result = null;
        $instances = static::getContainer()->instances;
        foreach ($instances as $instance) {
            if (StrUtil::equals($instance->class, $class)) {
                $result = $instance->proxy;
            }
        }
        if (ObjUtil::isNotNull($result)) {
            return $result;
        }
        throw new RuntimeException("undefined instances");
    }

    /**
     * 获取容器中的对象实例数量
     * @param string $class
     * @param string|null $method
     * @return int
     */
    public static function getCountClass($class, $method = null)
    {
        $count = 0;
        if ($method !== null) {
            $class = $class . "::" . $method;
        }
        $instances = static::getContainer()->instances;
        foreach ($instances as $instance) {
            if (StrUtil::equals($instance->class, $class)) {
                ++$count;
            }
        }
        return $count;
    }

    /**
     * 获取容器中的对象实例
     * @param string $fatherClass
     * @return mixed
     */
    public static function getFatherClass($fatherClass)
    {
        $result = null;
        $instances = static::getContainer()->instances;
        foreach ($instances as $instance) {
            if (isset($instance->fatherClass)) {
                if (StrUtil::equals($instance->fatherClass->name, $fatherClass)) {
                    $result = $instance->proxy;
                }
            }
        }
        if (ObjUtil::isNotNull($result)) {
            return $result;
        }
        throw new RuntimeException("undefined instances");
    }

    /**
     * 获取容器中的对象实例通过接口类
     * @param string $fatherClass
     * @return array
     */
    public static function getFatherClasss($fatherClass)
    {
        $results = [];
        $instances = static::getContainer()->instances;
        foreach ($instances as $instance) {
            if (isset($instance->fatherClass)) {
                if (StrUtil::equals($instance->fatherClass->name, $fatherClass)) {
                    $results[] = $instance->proxy;
                }
            }
        }
        return $results;
    }

    /**
     * 绑定一个类、闭包、实例、接口实现到容器
     * @param array $options 参数
     * @return Instance
     */
    public static function set($options = [
        "concrete" => null, // 容器类
        "name" => null, // 容器名称
        "vars" => [], // 实例化的参数
        "remarks" => null, // 备注
        "init" => true, // 立即初始化
        "cover" => false, // 存在是否覆盖
        "newInstance" => false, // 是否每次调用都重新实例化
        "construct" => null, // 构造函数
        "parentInstance" => null, //父级实例
    ])
    {
        $container = static::getContainer();
        $log_time_start = microtime(true);
        $concrete = isset($options["concrete"]) ? $options["concrete"] : null;
        $name = isset($options["name"]) ? $options["name"] : null;
        $newInstance = isset($options['newInstance']) ? $options['newInstance'] : false;
        $cover = isset($options['cover']) && $options['cover'];
        $init = isset($options['init']) ? $options['init'] : true;
        $vars = isset($options['vars']) ? $options['vars'] : [];
        $construct = isset($options['construct']) ? $options['construct'] : null;
        $parentInstance = isset($options['parentInstance']) ? $options['parentInstance'] : null;
        $currentDate = date("Y-m-d H:i:s");
        if (static::has($name)) {
            // 有没有定义自己名称的优先
            $instance = $container->getInstanceByName($name);
        } else if (static::hasInitInstance($concrete)) {
            // 有没有预加载的优先加载预加载
            $instance = static::getInitInstance($concrete);
        } else {
            // 保底初始化
            $instance = new Instance();
        }
        $instance->name = $name;
        $instance->construct = $construct;
        $instance->newInstance = $newInstance;
        $instance->type = ReflectionUtil::getType($concrete);
        $instance->namespace = ReflectionUtil::getNamespace($concrete);
        $instance->usages = ReflectionUtil::getUsages($concrete);
        $instance->annotations = ReflectionUtil::getAnnotations($concrete);
        $instance->updateTime = $currentDate;
        $instance->parentInstance = $parentInstance;
        if (isset($options['remarks'])) {
            $instance->remarks = $options['remarks'];
        }
        if (ObjUtil::isNull($instance->createTime)) {
            $instance->createTime = $currentDate;
        }
        if (ObjUtil::isNull($instance->proxy)) {
            $instance->proxy = new ContainerProxyClass(static::$container, $instance->id, new ProxyClassCallback());
            $container->instances[$instance->id] = &$instance;
        }
        if ($instance->init !== true) {
            $instance->init = $init;
        }
        if (in_array(ReflectionUtil::getType($concrete), ["class", "object"])) {
            $instance->class = ReflectionUtil::getClass($concrete);
            $instance->fatherClass = ReflectionUtil::getFatherClass($concrete);
        }
        $container->instances[$instance->id] = &$instance;
        if ($init) {
            if (ObjUtil::isNull($instance->instance)) {
                $instance->instance = ReflectionUtil::instance($concrete, $vars);
                $instance = static::autowired($instance);
            } else if ($cover) {
                $instance->instance = ReflectionUtil::instance($concrete, $vars);
                $instance = static::autowired($instance);
            } else if ($newInstance) {
                $instance->instance = ReflectionUtil::instance($concrete, $vars);
                $instance = static::autowired($instance);
            }
            $container->instances[$instance->id] = &$instance;
            if (ObjUtil::isNotNull($construct)) {
                $construct($vars, $instance->instance);
            }
        }
        $instance->concrete = $concrete;
        $instance->interfaces = ReflectionUtil::getInterfaces($concrete);
        $container->instances[$instance->id] = &$instance;
//        if ($init) {
//            \maotool\log\Console::debug('框架容器 ' . $instance->class . ' 加载用时: ' . number_format(microtime(true) - $log_time_start, 10) . ' 秒。');
//        } else {
//            \maotool\log\Console::debug('框架容器 ' . $instance->class . ' 预加载用时: ' . number_format(microtime(true) - $log_time_start, 10) . ' 秒。');
//        }
        return $instance;
    }

    /**
     * 注册容器中的对象实例
     * @param $instance Instance
     * @return Instance
     */
    public static function autowired($instance)
    {
        $reflect = ReflectionUtil::reflectionClass($instance->instance);
        foreach ($reflect->getProperties() as $property) {
            $property->setAccessible(true);
            $annotation = ReflectionUtil::getAnnotationByClass($property, Autowired::class);
            $returnClass = ReflectionUtil::getReturnClass($property);
            if (isset($annotation) && isset($returnClass)) {
                if (count($returnClass->value) > 0) {
                    $autowiredClass = ReflectionUtil::getUsagesClass($property->class, $returnClass->value[0]);
                    if (isset($autowiredClass)) {
                        if (self::hasInstanceByClass($autowiredClass)) {
                            $property->setValue($instance->instance, self::getInstanceByClass($autowiredClass)->proxy);
                        } else {
                            $property->setValue($instance->instance, self::set(["concrete" => $autowiredClass, "init" => false])->proxy);
                        }
                    }
                }
            }
        }
        return $instance;
    }

    /**
     * 移除容器中的对象实例
     * @param string $name
     * @return bool
     */
    public static function delete($name)
    {
        $count = 0;
        $instances = static::getContainer()->instances;
        foreach ($instances as $instance) {
            if (StrUtil::equals(StrUtil::lowerCase($instance->name), StrUtil::lowerCase($name))) {
                ++$count;
                unset(static::getContainer()->instances[$instance->id]);
            }
        }
        return (bool)$count;
    }

    /**
     * 移除容器中的对象实例
     * @param string $class
     * @param string|null $method
     * @return bool
     */
    public static function deleteClass($class, $method = null)
    {
        $count = 0;
        if ($method !== null) {
            $class = $class . "::" . $method;
        }
        $instances = static::getContainer()->instances;
        foreach ($instances as $instance) {
            if (StrUtil::equals($instance->class, $class)) {
                ++$count;
                unset(static::getContainer()->instances[$instance->id]);
            }
        }
        return (bool)$count;
    }

    /**
     * 判断容器是否存在
     * @param string $name
     * @return bool
     */
    public static function has($name)
    {
        if (StrUtil::hasEmpty($name)) {
            return false;
        }
        $instances = static::getContainer()->instances;
        foreach ($instances as $instance) {
            if (StrUtil::equals(StrUtil::lowerCase($instance->name), StrUtil::lowerCase($name))) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断容器是否存在
     * @param string $class
     * @return bool
     */
    public static function hasClass($class)
    {
        $instances = static::getContainer()->instances;
        foreach ($instances as $instance) {
            if (StrUtil::equals($instance->class, $class)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取容器
     * @access public
     * @param string $name
     * @return Instance
     */
    public function getInstance($name)
    {
        return static::getInstanceByName($name);
    }

    /**
     * 获取容器
     * @param mixed $id
     * @return null
     */
    public function getInstanceById($id)
    {
        $result = null;
        $instances = static::getContainer()->instances;
        foreach ($instances as $instance) {
            if (StrUtil::equals($instance->id, $id)) {
                $result = $instance;
            }
        }
        if (ObjUtil::isNotNull($result)) {
            return $result;
        }
        throw new RuntimeException("undefined instances");
    }

    /**
     * 获取容器
     * @param mixed $name
     * @return null
     */
    public function getInstanceByName($name)
    {
        $result = null;
        $instances = static::getContainer()->instances;
        foreach ($instances as &$instance) {
            if (StrUtil::equals(StrUtil::lowerCase($instance->name), StrUtil::lowerCase($name))) {
                $result = $instance;
            }
        }
        if (ObjUtil::isNotNull($result)) {
            return $result;
        }
        throw new RuntimeException("undefined instances");
    }

    /**
     * 获取容器中的对象实例
     * @param string $class
     * @return Instance
     */
    public static function getInstanceByClass($class)
    {
        $result = null;
        $instances = static::getContainer()->instances;
        foreach ($instances as $instance) {
            if (StrUtil::equals($instance->class, $class)) {
                $result = $instance;
            }
        }
        if (ObjUtil::isNotNull($result)) {
            return $result;
        }
        throw new RuntimeException("undefined instances");
    }

    /**
     * 获取容器中的对象实例
     * @param string $annotation
     * @return Instance[]
     */
    public static function getInstanceByAnnotations($annotation)
    {
        $results = [];
        $instances = static::getContainer()->instances;
        foreach ($instances as $instance) {
            $isExist = false;
            foreach ($instance->annotations as $item) {
                if (StrUtil::equals(ReflectionUtil::getClass($item), $annotation)) {
                    $isExist = true;
                }
            }
            if ($isExist) {
                $results[] = $instance;
            }
        }
        return $results;
    }

    /**
     * 获取容器中的对象实例
     * @param string $concrete
     * @return Instance
     */
    public static function getInitInstance($concrete)
    {
        $result = null;
        $instances = static::getContainer()->instances;
        foreach ($instances as $instance) {
            if ($instance->init === false) {
                if (ReflectionUtil::getType($concrete) === "class") {
                    if (StrUtil::equals($instance->class, $concrete)) {
                        $result = $instance;
                    }
                }
            }
        }
        if (ObjUtil::isNotNull($result)) {
            return $result;
        }
        throw new RuntimeException("undefined instances");
    }

    /**
     * 获取容器中的对象实例数量
     * @param string $class
     * @param string|null $method
     * @return int
     */
    public static function getInstanceCountByClass($class, $method = null)
    {
        $count = 0;
        if ($method !== null) {
            $class = $class . "::" . $method;
        }
        $instances = static::getContainer()->instances;
        foreach ($instances as $instance) {
            if (StrUtil::equals($instance->class, $class)) {
                ++$count;
            }
        }
        return $count;
    }

    /**
     * 判断容器是否存在
     * @param $class
     * @param $method
     * @return bool
     */
    public static function hasInstanceByClass($class, $method = null)
    {
        if ($method !== null) {
            $class = $class . "::" . $method;
        }
        $instances = static::getContainer()->instances;
        foreach ($instances as $instance) {
            if (StrUtil::equals($instance->class, $class)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断容器是否存在
     * @param $concrete
     * @return bool
     */
    public static function hasInitInstance($concrete)
    {
        $instances = static::getContainer()->instances;
        foreach ($instances as $instance) {
            if ($instance->init === false) {
                if (ReflectionUtil::getType($concrete) === "class") {
                    if (StrUtil::equals($instance->class, $concrete)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * 获取容器中的对象实例
     * @param string $interface
     * @return Instance
     */
    public static function getInstanceByInterface($interface)
    {
        $result = null;
        $instances = static::getContainer()->instances;
        foreach ($instances as $instance) {
            foreach ($instance->interfaces as $item) {
                if (StrUtil::equals($item->name, $interface)) {
                    $result = $instance;
                }
            }
        }
        if (ObjUtil::isNotNull($result)) {
            return $result;
        }
        throw new RuntimeException("undefined instances");
    }

    /**
     * 获取容器中的对象实例
     * @access public
     * @return array
     */
    public function all()
    {
        return static::getContainer()->instances;
    }

    /**
     * 清除容器中的对象实例
     * @access public
     * @return bool
     */
    public function flush()
    {
        if (static::count()) {
            static::getContainer()->instances = [];
            return true;
        } else {
            return false;
        }
    }

    /**
     * 获取容器数量
     * @return int
     */
    public function count()
    {
        return count(static::getContainer()->instances);
    }

}